/**
 * 将联合类型转换为交叉类型
 */
type UnionToIntersection<U> = (U extends any ? (_: U) => void : never) extends (_: infer I) => void
  ? I
  : never;

/**
 * 合并对象类型
 */
type Merge<T> =
  T extends Record<string, any>
    ? {
        [K in keyof T]: T[K];
      }
    : never;

/**
 * 创建枚举类型
 */
type CreateEnum<T> = T extends [infer E, infer K, unknown]
  ? {
      [P in E & string]: K;
    }
  : never;

/**
 * 判断类型是字符串还是数字
 */
type StringOrNum<T> = T extends string ? string : T extends number ? number : never;

/**
 * 创建枚举对象
 * @param args [描述, 键, 标签]
 * @returns 枚举对象
 * @example
 * ```js
 * const statusEnum = createEnum([
 *   ['START_STATUS', '0', '开始'],
 *   ['PEDDLING_STATUS', '1', '进行中'],
 *   ['END_STATUS', '2', '结束'],
 * ]);
 * ```
 */
export function createEnum<
  K extends Uppercase<string>,
  V extends string | number,
  L,
  T extends [K, V, L],
>(
  args: readonly T[],
): Merge<UnionToIntersection<CreateEnum<T>>> & {
  get: (value: StringOrNum<T[1]>) => T[2] | undefined;
  list: {
    value: StringOrNum<T[1]>;
    label: T[2];
    key_code: T[0];
  }[];
} {
  const obj = Object.create(null);
  const map = new Map<V, L>();
  const list: {
    value: V;
    label: L;
    key_code: T[0];
  }[] = [];

  for (const [d, k, l] of args) {
    obj[d] = k;
    map.set(k, l);
    list.push({
      value: k,
      label: l,
      key_code: d,
    });
  }
  return Object.freeze({
    ...obj,
    get: (key: V) => map.get(key),
    list,
  });
}

/**
 * 获取createEnum返回值的值类型
 */
export type CreateEnumValues<T extends ReturnType<typeof createEnum>> = Omit<
  T,
  'list' | 'get'
>[keyof Omit<T, 'list' | 'get'>];

/**
 * createEnum函数的类型
 */
type EnumClassType = typeof createEnum;

/**
 * createEnum工厂接口
 */
interface CreateEnumFactory extends EnumClassType {
  new <K extends Uppercase<string>, V extends string | number, L, T extends [K, V, L]>(
    args: T[],
  ): Merge<UnionToIntersection<CreateEnum<T>>> & {
    get: (value: StringOrNum<T[1]>) => T[2] | undefined;
    list: {
      value: StringOrNum<T[1]>;
      label: T[2];
      key_code: T[0];
    }[];
  };
}

/**
 * 枚举类
 */
export const EnumClass: CreateEnumFactory = createEnum as unknown as CreateEnumFactory;
